/*
 * linux/drivers/video/backlight/pwm_bl.c
 *
 * simple PWM based backlight control, board code has to setup
 * 1) pin configuration so PWM waveforms can output
 * 2) platform_data being correctly configured
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/gpio/consumer.h>
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/backlight.h>
#include <linux/err.h>
#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/miscdevice.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <linux/cdev.h>

#include "hisi_pwm_lcd.h"
#include "hi_type.h"
#include <hi_mipi_tx.h>
//#include <mipi_tx_hal.h>

extern void mipi_tx_enable(void);
extern int mipi_tx_set_cmd(cmd_info_t *cmd_info);
extern int mipi_tx_set_combo_dev_cfg(combo_dev_cfg_t *p_dev_cfg);


/* Lcd pwm clock Control */
void __iomem *reg_iocfg188_base_va;
void __iomem *reg_crg_base_va;

/* Lcd reset Pin Control */
void __iomem *reg_soc_gpio0_dir_va;
void __iomem *reg_soc_gpio0_dat_va;

/* Lcd pwm backlight */
#ifdef CONFIG_USING_SHUB_GPIO
void __iomem *reg_shub_gpio3_dir_va;
void __iomem *reg_shub_gpio3_dat_va;
#else
void __iomem *reg_shub_pwm1_cfg0_va;
void __iomem *reg_shub_pwm1_cfg1_va;
void __iomem *reg_shub_pwm1_cfg2_va;
void __iomem *reg_shub_pwm1_ctrl_va;
void __iomem *reg_shub_pwm1_state0_va;
void __iomem *reg_shub_pwm1_state1_va;
void __iomem *reg_shub_pwm1_state2_va;
#endif

/* Only for test */
#if (DEBUG_LEVEL >= 3)
void __iomem *reg_soc_gpio17_dir_va;
void __iomem *reg_soc_gpio17_dat_va;
#endif


static hisi_pwm_backlight_data pb_data;

static void* get_Hisi_PwmBacklight_Data(void)
{
    return &pb_data;
}

static lcd_register_table mipi_tx_reg[] =
{
#if 0
    {0x01,  1, {}},
    {0xff,  6, {0x77,0x01,0x00,0x00,0x11}},
    {0xd1,  2, {0x11}},
    {0x11,  1, {}},
    {0xff,  6, {0x77,0x01,0x00,0x00,0x10}},
    {0xc0,  3, {0x63,0x00}},
    {0xc1,  3, {0x09,0x02}},
    {0xc2,  3, {0x31,0x08}},
    {0xc7,  2, {0x04}},
    {0xb0, 17, {0x00,0x11,0x19,0x0c,0x10,0x06,0x07,0x0a,0x09,0x22,0x04,0x10,0x0e,
0x28,0x30,0x1c}},
    {0xb1, 17, {0x00,0x12,0x19,0x0d,0x10,0x04,0x06,0x07,0x08,0x23,0x04,0x12,0x11,
0x28,0x30,0x1c}},
    {0xff,  6, {0x77,0x01,0x00,0x00,0x11}},
    {0x80,  2, {0x4d}},
    {0xb1,  2, {0x66}},
    {0xb2,  2, {0x07}},
    {0xb3,  2, {0x80}},
    {0xb5,  2, {0x47}},
    {0xb7,  2, {0x8a}},
    {0xb8,  2, {0x21}},
    {0xc1,  2, {0x78}},
    {0xc2,  2, {0x78}},
    {0xd0,  2, {0x88}}, 
    {0xe0,  2, {0x00,0x00,0x02}},//new from FAE sent one of two.
    {0xe1, 12, {0x01,0xa0,0x03,0xa0,0x02,0xa0,0x04,0xa0,0x00,0x44,0x44}},
    {0xe2, 13, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}},
    {0xe3,  5, {0x00,0x00,0x33,0x33}},
    {0xe4,  3, {0x44,0x44}},
    {0xe8, 17, {0x02,0x26,0xa0,0xa0,0x04,0x28,0xa0,0xa0,0x06,0x2a,0xa0,0xa0,0x08,
0x2c,0xa0,0xa0}},
    {0xeb,  8, {0x00,0x00,0xe4,0xe4,0x44,0x00,0x40}},
    {0xed, 17, {0xff,0xf7,0x65,0x4f,0x0b,0xa1,0xcf,0xff,0xff,0xfc,0x1a,0xb0,0xf4,
0x56,0x7f,0xff}},
    {0xff,  6, {0xff,0x77,0x01,0x00,0x00}},
    {0x36,  2, {0x10}}, 
    {0x29,  1, {}},

#else

    {0x01 ,1 ,0x00},
    {0xff , 2, 0x77},
    {0xff , 2, 0x01},
    {0xff , 2,0x00},
    {0xff , 2,0x00},
    {0xff , 2,0x11},
    {0xd1 , 2,0x11},
    {0x11 , 1,0},
    {0xff , 2,0x77},
    {0xff , 2,0x01},
    {0xff , 2,0x00},
    {0xff , 2,0x00},
    {0xff , 2,0x10},
    {0xd1 , 2,0x11},
    {0xc0 , 2,0x63},
    {0xc0 , 2,0x00},
    {0xc1 , 2,0x09},
    {0xc1 , 2,0x02},    
    {0xc2 , 2,0x31},
    {0xc2 , 2,0x08},
    {0xc7 , 2,0x04},    
    {0xb0 , 2,0x00},
    {0xb0 , 2,0x11},
    {0xb0 , 2,0x19},        
    {0xb0 , 2,0x0c},
    {0xb0 , 2,0x10},
    {0xb0 , 2,0x06},
    {0xb0 , 2,0x07},
    {0xb0 , 2,0x0a},
    {0xb0 , 2,0x09},
    {0xb0 , 2,0x22},    
    {0xb0 , 2,0x04},
    {0xb0 , 2,0x10},
    {0xb0 , 2,0x0e},
    {0xb0 , 2,0x28},
    {0xb0 , 2,0x30},
    {0xb0 , 2,0x1c},
    {0xb1 , 2,0x00},
    {0xb1 , 2,0x12},
    {0xb1 , 2,0x19},        
    {0xb1 , 2,0x0d},
    {0xb1 , 2,0x10},
    {0xb1 , 2,0x04},
    {0xb1 , 2,0x06},
    {0xb1 , 2,0x07},
    {0xb1 , 2,0x08},
    {0xb1 , 2,0x23},    
    {0xb1 , 2,0x04},
    {0xb1 , 2,0x12},
    {0xb1 , 2,0x11},
    {0xb1 ,2, 0x28},
    {0xb1 ,2, 0x30},
    {0xb1 ,2, 0x1c},
    {0xff ,2, 0x77},
    {0xff ,2, 0x01},
    {0xff ,2, 0x00},
    {0xff ,2,   0x00},
    {0xff ,2,   0x11},
    {0x80 ,2,    0x4d},
    {0xb1 ,2,    0x66},
    {0xb2 ,2,    0x07},
    {0xb3 ,2,    0x80},
    {0xb5 ,2,    0x47},
    {0xb7 ,2,    0x8a},
    {0xb8 ,2,    0x21},
    {0xc1 ,2,    0x78},
    {0xc2 ,2,    0x78},
    {0xd0 ,2,    0x88}, 
    {0xe0 ,2,    0x00},
    {0xe0 ,2,    0x00},
    {0xe0 ,2,    0x02}, 
    {0xe1 ,2,    0x01},
    {0xe1 ,2,    0xa0},
    {0xe1 ,2,    0x03}, 
    {0xe1 ,2,  0xa0},
    {0xe1 ,2,  0x02},
    {0xe1 ,2,  0xa0}, 
    {0xe1 ,2, 0x04},
    {0xe1 ,2,  0xa0},
    {0xe1 ,2,  0x00}, 
    {0xe1 ,2,  0x44},
    {0xe1 ,2,  0x44},   
    {0xe2 ,2,  0x00},
    {0xe2 ,2, 0x00},
    {0xe2 ,2,  0x00}, 
    {0xe2 ,2,  0x00},
    {0xe2 ,2,  0x00},
    {0xe2 ,2, 0x00}, 
    {0xe2 ,2,  0x00},
    {0xe2 ,2,  0x00},
    {0xe2 ,2,  0x00}, 
    {0xe2 ,2,  0x00},
    {0xe2 ,2,  0x00},   
    {0xe2 ,2,  0x00}, 
    {0xe3 ,2,  0x00}, 
    {0xe3 ,2,  0x00},
    {0xe3 ,2,  0x33}, 
    {0xe3 ,2,  0x33}, 
    {0xe4 ,2,  0x44}, 
    {0xe4 ,2, 0x44},
    {0xe8 ,2,  0x02},
    {0xe8 ,2, 0x26},
    {0xe8 ,2,  0xa0}, 
    {0xe8 ,2,  0xa0},
    {0xe8 ,2,  0x04}, 
    {0xe8 ,2,  0x28},
    {0xe8 ,2,  0xa0},
    {0xe8 ,2 ,0xa0}, 
    {0xe8 ,2 , 0x06},
    {0xe8 ,2 , 0x2a},   
    {0xe8 ,2,  0xa0}, 
    {0xe8 ,2, 0xa0}, 
    {0xe8 ,2,  0x08},
    {0xe8 ,2,  0x2c}, 
    {0xe8 ,2,  0xa0}, 
    {0xe8 ,2,  0xa0},
    {0xeb ,2,  0x00}, 
    {0xeb ,2,  0x00}, 
    {0xeb ,2,  0xe4},
    {0xeb ,2,  0xe4}, 
    {0xeb ,2,  0x44}, 
    {0xeb ,2, 0x00},
    {0xeb ,2,  0x40},   
    {0xed ,2,  0xff},
    {0xed ,2,  0xf7},
    {0xed ,2,  0x65}, 
    {0xed ,2,  0x4f},
    {0xed ,2,  0x0b},
    {0xed ,2,  0xa1}, 
    {0xed ,2,  0xcf},
    {0xed ,2,  0xff},
    {0xed ,2,  0xff}, 
    {0xed ,2,  0xfc},
    {0xed ,2,  0x1a},   
    {0xed ,2,  0xb0}, 
    {0xed ,2,  0xf4}, 
    {0xed ,2,  0x56},
    {0xed ,2 , 0x7f}, 
    {0xed ,2 , 0xff}, 
    {0xff ,2 ,0xff}, 
    {0xff ,2 , 0x77},
    {0xff ,2 , 0x01}, 
    {0xff ,2 , 0x00}, 
    {0xff ,2 , 0x00}, 
    {0x36 ,2 , 0x10}, 
    {0x29 ,1  }, 

#endif
};

static int hisi_lcd_st7701s_reset(void)
{
    int val = 0;

    // Check GPIO output dir
    val = HISI_REG_READ(reg_soc_gpio0_dir_va);
    LOGD("GPIO0_DIR=%x\n",val);
    val |= (0x1 << 2);
    HISI_REG_WRITE(reg_soc_gpio0_dir_va,val);
    val = HISI_REG_READ(reg_soc_gpio0_dir_va);
    LOGD("GPIO0_DIR=%x\n",val);
    
    // Config GPIO3_1 Pin default output low
    val = HISI_REG_READ(reg_soc_gpio0_dat_va);
    LOGD("GPIO0_DAT=%x\n",val);
    val &= ~(0x1 << 2);
    HISI_REG_WRITE(reg_soc_gpio0_dat_va, val);
    val = HISI_REG_READ(reg_soc_gpio0_dat_va);
    LOGD("GPIO0_DAT=%x\n",val);

    msleep(20);
    
    val |= (0x1 << 2);
    HISI_REG_WRITE(reg_soc_gpio0_dat_va, val);

    return 0;
}

static int hisi_lcd_st7701s_init(void)
{
    int i = 0;
    int ret = 0;
    //int val = 0;

    combo_dev_cfg_t *pstcombo_dev_cfg = NULL;
    cmd_info_t *cmd_info = NULL;

    pstcombo_dev_cfg = (combo_dev_cfg_t *)kzalloc(sizeof(combo_dev_cfg_t),
GFP_KERNEL);
    cmd_info = (cmd_info_t *)kzalloc(sizeof(cmd_info_t),GFP_KERNEL);
    
#if 1
    pstcombo_dev_cfg->devno         = 0;
    pstcombo_dev_cfg->lane_id[0]        = 1 ;

    pstcombo_dev_cfg->lane_id[1]        = 1 ;
    pstcombo_dev_cfg->output_format = OUT_FORMAT_RGB_16_BIT;
    pstcombo_dev_cfg->output_mode   = OUTPUT_MODE_DSI_CMD;
    pstcombo_dev_cfg->video_mode    = NON_BRUST_MODE_SYNC_PULSES;
    pstcombo_dev_cfg->phy_data_rate = 20; /*mbps*/
    pstcombo_dev_cfg->pixel_clk     = 1000000; /*KHz*/

    pstcombo_dev_cfg->sync_info.vid_pkt_size     = 1;

    pstcombo_dev_cfg->sync_info.vid_hbp_pixels   = 20;
    pstcombo_dev_cfg->sync_info.vid_hline_pixels = 20;
    pstcombo_dev_cfg->sync_info.vid_hsa_pixels   = -20; 
    
    pstcombo_dev_cfg->sync_info.vid_vbp_lines    = 10;
    pstcombo_dev_cfg->sync_info.vid_vfp_lines    = 10;
    pstcombo_dev_cfg->sync_info.vid_vsa_lines    = -10; 
    pstcombo_dev_cfg->sync_info.vid_active_lines = 460;

    pstcombo_dev_cfg->sync_info.edpi_cmd_size     = 1;
#endif  

    mipi_tx_enable();

    ret = mipi_tx_set_combo_dev_cfg(pstcombo_dev_cfg);
    if (ret < 0)
    {
        LOGE("mipi_tx set combo_dev config failed!\n");
        ret = -1;
    }

    for(i=0 ; i<146 ; i++ )
    {
#if 1
            cmd_info->devno         =   3;
            cmd_info->cmd           =   mipi_tx_reg[i].data;
            cmd_info->data_type     =   mipi_tx_reg[i].reg;
            cmd_info->cmd_size      =   mipi_tx_reg[i].lenth;
#endif      
            if(i >=1 && (mipi_tx_reg[i].reg != mipi_tx_reg[i-1].reg))
            {
                //osal_msleep(120);
                //LOGI("next register group ret =%d !!\n",ret);  
            }

            ret = mipi_tx_set_cmd(cmd_info);
            if (ret < 0)
            {
                LOGE("mipi_tx set cmd failed!\n");
                ret = -1;
            }
    }   
    pstcombo_dev_cfg->output_mode   =   OUTPUT_MODE_DSI_VIDEO;  
    ret = mipi_tx_set_combo_dev_cfg(pstcombo_dev_cfg);
    if (ret < 0)
    {
        LOGE("mipi_tx set combo_dev config failed!\n");
        ret = -1;
    }

    kfree(pstcombo_dev_cfg);
    kfree(cmd_info);
      
    return 0;
}

#if (DEBUG_LEVEL >= 3)
static int hisi_led_debug_enable(bool enable)
{
    int val;

    LOGD("++\n");

    val = HISI_REG_READ(reg_soc_gpio17_dir_va);
	LOGD("GPIO17_DIR=%x\n",val);
    val |= (0x1 << 5);
    HISI_REG_WRITE(reg_soc_gpio17_dir_va, val);
	val = HISI_REG_READ(reg_soc_gpio17_dir_va);
	LOGD("GPIO17_DIR=%x\n",val);

    if(enable)
    {
        // Config GPIO3_1 Pin output High
        val = HISI_REG_READ(reg_soc_gpio17_dat_va);
    	LOGD("GPIO17_DAT=%x\n",val);

        val |= (0x1 << 5);
        HISI_REG_WRITE(reg_soc_gpio17_dat_va, val);

    	val = HISI_REG_READ(reg_soc_gpio17_dat_va);
    	LOGD("GPIO17_DAT=%x\n",val);
    }
    else
    {
        // Config GPIO3_1 Pin output Low
        val = HISI_REG_READ(reg_soc_gpio17_dat_va);
    	LOGD("GPIO17_DAT=%x\n",val);

        val &= ~(0x1 << 5);
        HISI_REG_WRITE(reg_soc_gpio17_dat_va, val);

    	val = HISI_REG_READ(reg_soc_gpio17_dat_va);
    	LOGD("GPIO17_DAT=%x\n",val);
    }
	

    LOGD("--\n\n");
    return 0;
}
#endif

static int hisi_pwm_backlight_enable(void)
{
	int val;

	LOGD("++\n");

    val = HISI_REG_READ(reg_shub_pwm1_ctrl_va);
    val |= (0x1 << 0);
    HISI_REG_WRITE(reg_shub_pwm1_ctrl_va, val);

	LOGD("--\n\n");
    return 0;
}

static int hisi_pwm_backlight_disable(void)
{
    int val = 0 ;

    LOGD("++\n");

    val = HISI_REG_READ(reg_shub_pwm1_ctrl_va);
    val &= ~(0x1 << 0);
    HISI_REG_WRITE(reg_shub_pwm1_ctrl_va, val);

	LOGD("--\n\n");
    return 0;
}

static int hisi_pwm_backlight_compute_duty_cycle(int brightness)
{
	int duty_cycle;
    hisi_pwm_backlight_data *pb = NULL;

	LOGD("++\n");

    pb = (hisi_pwm_backlight_data*)get_Hisi_PwmBacklight_Data();
    if(brightness < 0)
	{
        pb->cur_brightness = 0;
	}
    else if(brightness > pb->max_brightness)
	{
        pb->cur_brightness = pb->max_brightness;
	}
    else
	{
        pb->cur_brightness = brightness;
	}
    
    duty_cycle = (1000 * pb->cur_brightness) / 100;

	LOGD("--\n\n");

	return duty_cycle;
}

static int hisi_pwm_backlight_update_status(int brightness)
{
    int val = 0 ;

	LOGD("++\n");

    val = hisi_pwm_backlight_compute_duty_cycle(brightness);
    HISI_REG_WRITE(reg_shub_pwm1_cfg2_va, val);
    
	LOGD("--\n\n");
	return 0;
}

static int hisi_pwm_clk_sel_config(void)
{
    int val = 0;

    val = HISI_REG_READ(reg_crg_base_va);

    // PWM reset quest check
    //if(val & (0x1 << 18))
    //{
    //    val &= ~(0x1 << 18);
    //}
    //else
    //{
    //    val |= (0x1 << 18);
    //    msleep(1);
    //    val &= ~(0x1 << 18);
    //}

    // PWM Clk Control Reg bit = Open
    val |= (0x1 << 19); 

    // PWM Clock Select: 50M
    val &= ~(0x1 << 20); 
    val |= (0x1 << 21); 

    // Write config value to Register
    HISI_REG_WRITE(reg_crg_base_va, val);
    
    return 0;
}

static int hisi_pwm_backlight_iocfg_init(void)
{
    int val = 0;
    
    LOGD("++\n");
    
    /* Config AT4/GPIO3_1 Pin as pwm output */
    val = HISI_REG_READ(reg_iocfg188_base_va);
    LOGD("IOCFG188_REG=%x\n",val);

    // bit[0 - 3]: SHUB PWM Output Mode
    val |= (0x1 << 0);
    val &= ~(0x1 << 1);
    val &= ~(0x1 << 2);
    val &= ~(0x1 << 3);

    // bit8: Up for power enable
    //val |= (0x1 << 8);

    // bit10: power level convtor rate =  fast
    //val &= ~(0x1 << 10);

    // bit12: Disable input Mode
    //val &= ~(0x1 << 12);

    // bit14: Force up power
    //val |= (0x1 << 14);
    
    HISI_REG_WRITE(reg_iocfg188_base_va,val);
    val = HISI_REG_READ(reg_iocfg188_base_va);
    LOGD("IOCFG188_REG=%x\n",val);    

    LOGD("--\n\n");
    return 0;
}

static int hisi_pwm_backlight_pwmout_init(int dft_brightness)
{
    int val = 0;
    
    LOGD("++\n");


#ifdef CONFIG_USING_SHUB_GPIO
    val = HISI_REG_READ(reg_shub_gpio3_dir_va);
    LOGD("GPIO3_DIR=%x\n",val);
    val |= (0x1 << 1);
    HISI_REG_WRITE(reg_shub_gpio3_dir_va,val);
    val = HISI_REG_READ(reg_shub_gpio3_dir_va);
    LOGD("GPIO3_DIR=%x\n",val);
    
    // Config GPIO3_1 Pin default output low
    val = HISI_REG_READ(reg_shub_gpio3_dat_va);
    LOGD("GPIO3_DAT=%x\n",val);
    val &= ~(0x1 << 1);
    HISI_REG_WRITE(reg_shub_gpio3_dat_va, val);
    val = HISI_REG_READ(reg_shub_gpio3_dat_va);
    LOGD("GPIO3_DAT=%x\n",val);
#else
    // PWM config Start.
    // Step 1. Check PWM free or not 
    val = HISI_REG_READ(reg_shub_pwm1_state2_va);
    if(val &(0x1 << 10))
    {
        hisi_pwm_backlight_disable();
    }

    // Step 2. Config 50KHz clock output
    HISI_REG_WRITE(reg_shub_pwm1_cfg0_va, 0x000003E8);

    // Step 3. Config output alway
    val = HISI_REG_READ(reg_shub_pwm1_ctrl_va);
    val |= (0x1 << 2);
    HISI_REG_WRITE(reg_shub_pwm1_ctrl_va, val);
    
    // Step 4. Config default output freq
    val = hisi_pwm_backlight_compute_duty_cycle(dft_brightness);
    HISI_REG_WRITE(reg_shub_pwm1_cfg2_va, val);

    hisi_pwm_backlight_enable();

    LOGD("SHUB_PWM1_CFG0 = %x\n",HISI_REG_READ(reg_shub_pwm1_cfg0_va));
    LOGD("SHUB_PWM1_CFG1 = %x\n",HISI_REG_READ(reg_shub_pwm1_cfg1_va));
    LOGD("SHUB_PWM1_CFG2 = %x\n",HISI_REG_READ(reg_shub_pwm1_cfg2_va));
    LOGD("SHUB_PWM1_CTRL = %x\n",HISI_REG_READ(reg_shub_pwm1_ctrl_va));
    LOGD("SHUB_PWM1_STATE0 = %x\n",HISI_REG_READ(reg_shub_pwm1_state0_va));
    LOGD("SHUB_PWM1_STATE1 = %x\n",HISI_REG_READ(reg_shub_pwm1_state1_va));
    LOGD("SHUB_PWM1_STATE2 = %x\n",HISI_REG_READ(reg_shub_pwm1_state2_va));

#endif
    LOGD("--\n\n");
    return 0;
}


static long hisi_pwm_backlight_ioctl( struct file *file, 
                                     unsigned int cmd, 
                                     unsigned long arg )
{
    if((cmd >= 0) && (cmd <= 100))
    {
        hisi_pwm_backlight_update_status(cmd);
        return 0;
    }
    else
    {
        LOGE("Invild brightness !");
        return -EINVAL;
    }
}

static struct file_operations hisi_pwm_backlight_dev_fops = 
{
    .owner = THIS_MODULE,
    .unlocked_ioctl = hisi_pwm_backlight_ioctl,
};

static struct miscdevice hisi_pwm_backlight_misc =
{
    .minor = DEVICE_MINOR,
    .name = DEVICE_NAME,
    .fops = &hisi_pwm_backlight_dev_fops,
};

static int hisi_reg_ioremap_init(void)
{
    /* Lcd pwm clock Control */
	reg_iocfg188_base_va = ioremap_nocache((unsigned long)IOCFG188_REG_BASE, 4);
    if (!reg_iocfg188_base_va)
    {
        LOGE("Kernel: ioremap reg_iocfg188_base_va failed!\n");
        return -ENOMEM;
    }

    reg_crg_base_va = ioremap_nocache((unsigned long)PERI_CRG101_BASE,4);
    if (!reg_crg_base_va)
    {
        LOGE("Kernel: ioremap reg_crg_base_va failed!\n");
        return -ENOMEM;
    }

/* Lcd reset Pin Control */
    reg_soc_gpio0_dir_va = ioremap_nocache((unsigned long)SOC_GPIO0_DIR,4);
    if (!reg_soc_gpio0_dir_va)
    {
        LOGE("Kernel: ioremap reg_soc_gpio0_dir_va failed!\n");
        return -ENOMEM;
    }

    reg_soc_gpio0_dat_va = ioremap_nocache((unsigned long)SOC_GPIO0_DAT,4);
    if (!reg_soc_gpio0_dat_va)
    {
        LOGE("Kernel: ioremap reg_soc_gpio0_dat_va failed!\n");
        return -ENOMEM;
    }

/* Lcd pwm backlight */
#ifdef CONFIG_USING_SHUB_GPIO
    reg_shub_gpio3_dir_va = ioremap_nocache((unsigned long)SHUB_GPIO3_DIR,4);
    if (!reg_shub_gpio3_dir_va)
    {
        LOGE("Kernel: ioremap reg_shub_gpio3_dir_va failed!\n");
        return -ENOMEM;
    }
    reg_shub_gpio3_dat_va = ioremap_nocache((unsigned long)SHUB_GPIO3_DAT,4);
    if (!reg_shub_gpio3_dat_va)
    {
        LOGE("Kernel: ioremap reg_shub_gpio3_date_va failed!\n");
        return -ENOMEM;
    }
#else
    reg_shub_pwm1_cfg0_va = ioremap_nocache((unsigned long)SHUB_PWM1_CFG0,4);
    if (!reg_shub_pwm1_cfg0_va)
    {
        LOGE("Kernel: ioremap reg_shub_pwm1_cfg0_va failed!\n");
        return -ENOMEM;
    }
    reg_shub_pwm1_cfg1_va = ioremap_nocache((unsigned long)SHUB_PWM1_CFG1,4);
    if (!reg_shub_pwm1_cfg1_va)
    {
        LOGE("Kernel: ioremap reg_shub_pwm1_cfg1_va failed!\n");
        return -ENOMEM;
    }
    reg_shub_pwm1_cfg2_va = ioremap_nocache((unsigned long)SHUB_PWM1_CFG2,4);
    if (!reg_shub_pwm1_cfg2_va)
    {
        LOGE("Kernel: ioremap reg_shub_pwm1_cfg2_va failed!\n");
        return -ENOMEM;
    }
    reg_shub_pwm1_ctrl_va = ioremap_nocache((unsigned long)SHUB_PWM1_CTRL,4);
    if (!reg_shub_pwm1_ctrl_va)
    {
        LOGE("Kernel: ioremap reg_shub_pwm1_base_va failed!\n");
        return -ENOMEM;
    }
    reg_shub_pwm1_state0_va = ioremap_nocache((unsigned long)SHUB_PWM1_STATE0,4);
    if (!reg_shub_pwm1_state0_va)
    {
        LOGE("Kernel: ioremap reg_shub_pwm1_state0_va failed!\n");
        return -ENOMEM;
    }
    reg_shub_pwm1_state1_va = ioremap_nocache((unsigned long)SHUB_PWM1_STATE1,4);
    if (!reg_shub_pwm1_state1_va)
    {
        LOGE("Kernel: ioremap reg_shub_pwm1_state1_va failed!\n");
        return -ENOMEM;
    }
    reg_shub_pwm1_state2_va = ioremap_nocache((unsigned long)SHUB_PWM1_STATE2,4);
    if (!reg_shub_pwm1_state2_va)
    {
        LOGE("Kernel: ioremap reg_shub_pwm1_base_va failed!\n");
        return -ENOMEM;
    }
#endif

/* Only for test */
#if (DEBUG_LEVEL >= 3)
    reg_soc_gpio17_dir_va = ioremap_nocache((unsigned long)SOC_GPIO17_DIR,4);
    if (!reg_soc_gpio17_dir_va)
    {
        LOGE("Kernel: ioremap reg_soc_gpio17_dir_va failed!\n");
        return -ENOMEM;
    }
    reg_soc_gpio17_dat_va = ioremap_nocache((unsigned long)SOC_GPIO17_DAT,4);
    if (!reg_soc_gpio17_dat_va)
    {
        LOGE("Kernel: ioremap reg_soc_gpio17_dat_va failed!\n");
        return -ENOMEM;
    }
#endif

    return 0;
}

static int hisi_reg_iounmap_exit(void)
{
    /* Lcd pwm clock Control */
    iounmap((void*)reg_iocfg188_base_va);
    iounmap((void*)reg_crg_base_va);

    /* Lcd reset Pin Control */
    iounmap((void*)reg_soc_gpio0_dir_va);
    iounmap((void*)reg_soc_gpio0_dat_va);

    /* Lcd pwm backlight */
#ifdef CONFIG_USING_SHUB_GPIO
    iounmap((void*)reg_shub_gpio3_dir_va);
    iounmap((void*)reg_shub_gpio3_dat_va);
#else
    iounmap((void*)reg_shub_pwm1_cfg0_va);
    iounmap((void*)reg_shub_pwm1_cfg1_va);
    iounmap((void*)reg_shub_pwm1_cfg2_va);
    iounmap((void*)reg_shub_pwm1_ctrl_va);
    iounmap((void*)reg_shub_pwm1_state0_va);
    iounmap((void*)reg_shub_pwm1_state1_va);
    iounmap((void*)reg_shub_pwm1_state2_va);
#endif

/* Only for test */
#if (DEBUG_LEVEL >= 3)
    hisi_led_debug_enable(HI_FALSE);
    iounmap((void*)reg_soc_gpio17_dir_va);
    iounmap((void*)reg_soc_gpio17_dat_va);
#endif

    return 0;
}

static int __init hisi_pwm_backlight_init(void)
{
    int ret;
    //unsigned int i = 0;
    hisi_pwm_backlight_data *pbdata = &pb_data;

	printk("\n---- Build Time: %s %s ----\n\n",__DATE__,__TIME__);

	// Step 1. register misc driver 
    ret = misc_register(&hisi_pwm_backlight_misc); 
    if(ret < 0)
    {
        printk("Register misc device fiald!");
        return ret;
    }

    hisi_reg_ioremap_init();

    // Backlight init
    hisi_pwm_clk_sel_config();
    hisi_pwm_backlight_iocfg_init();

    pbdata->max_brightness = 100;
    pbdata->dft_brightness = 70;
    pbdata->cur_brightness = pbdata->dft_brightness;
    pbdata->enabled        = false;
    hisi_pwm_backlight_pwmout_init(pbdata->dft_brightness);

    // Lcd init
    hisi_lcd_st7701s_reset();
    msleep(1);
    hisi_lcd_st7701s_init();

    // Only for debug
#if (DEBUG_LEVEL >= 3)
    hisi_led_debug_enable(HI_TRUE);
#endif

	LOGD("--\n\n");

    return ret;
}

static void __exit hisi_pwm_backlight_exit(void)
{
    hisi_reg_iounmap_exit();
    misc_deregister(&hisi_pwm_backlight_misc);
}

module_init(hisi_pwm_backlight_init);
module_exit(hisi_pwm_backlight_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Li Shuliang");
MODULE_DESCRIPTION("PWM based Backlight Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:pwm-backlight");
